using Evaluant.Uss;
using Evaluant.Uss.Commands;
using System.Threading;
using Evaluant.Uss.Domain;
using Evaluant.Uss.PersistenceEngine.Contracts;

namespace Evaluant.Uss.Cache
{
	/// <summary>
	/// Visitor pattern to execute concrete commands
	/// </summary>
	class CacheCommandProcessor : ICommandProcessor
	{
        ReaderWriterLock _RWL;

		private IdentityMap _Entities;

		private string _NewId;
		public string NewId
		{
			get { return _NewId; }
			set { _NewId = value; }
		}

		public CacheCommandProcessor(IdentityMap entities, ReaderWriterLock rwl)
		{
			_Entities = entities;
            _RWL = rwl;
		}

		public void Process(Command c)
		{
		}

		public void Process(CreateEntityCommand c)
		{
			Entity entity = new Entity(c.Type);
			entity.Id = c.ParentId;
			entity.State = State.UpToDate;
			entity.MetaData = Entity.LoadMetaData.AttributesLoaded | Entity.LoadMetaData.AttributesLoaded;
            
            _RWL.AcquireWriterLock(Timeout.Infinite);
            try
            {
                _Entities.Add(Utils.GetCacheKey(c.Type, c.ParentId), entity);
            }
            finally
            {
                _RWL.ReleaseWriterLock();
            }

		}

		public void Process(CompoundCreateCommand c)
		{
			Process((CreateEntityCommand)c);
			foreach(CreateAttributeCommand cc in c.InnerCommands)
				Process(cc);
		}

		public void Process(CompoundUpdateCommand c)
		{
			foreach(Command cc in c.InnerCommands)
				cc.Accept(this);
		}

		public void Process(DeleteEntityCommand c)
		{
            _RWL.AcquireWriterLock(Timeout.Infinite);

            try
            {
                Entity processedEntity = _Entities[Utils.GetCacheKey(c.Type, c.ParentId)];
                if (processedEntity == null)
                    return;

                // Removes the entity from the cache
                _Entities.Remove(processedEntity);

                // Removes also all the references to this entity
                foreach(Entity e in _Entities)
                {
                    if (e != null)
                        e.RemoveReference(processedEntity);
                }
            }
            finally
            {
                _RWL.ReleaseWriterLock();
            }
		}

		public void Process(CreateAttributeCommand c)
		{
            _RWL.AcquireWriterLock(Timeout.Infinite);

            try
            {
                Entity entity = _Entities[Utils.GetCacheKey(c.ParentType, c.ParentId)];
                if (entity == null)
                    return;

                State tmpState = entity.State;
                entity.AddValue(c.Name, c.Value, c.Type, State.UpToDate);
                entity.State = tmpState;
            }
            finally
            {
                _RWL.ReleaseWriterLock();
            }

		}

		public void Process(UpdateAttributeCommand c)
		{
            _RWL.AcquireWriterLock(Timeout.Infinite);

            try
            {
                Entity entity = _Entities[Utils.GetCacheKey(c.ParentType, c.ParentId)];
                if (entity == null)
                    return;

                State tmpState = entity.State;
                entity[c.Name] = c.Value;
                entity.State = tmpState;
            }
            finally
            {
                _RWL.ReleaseWriterLock();
            }

		}

		public void Process(DeleteAttributeCommand c)
		{
            _RWL.AcquireWriterLock(Timeout.Infinite);

            try
            {
                Entity entity = _Entities[Utils.GetCacheKey(c.ParentType, c.ParentId)];
                if (entity == null)
                    return;

                State tmpState = entity.State;
                entity.RemoveAttribute(c.Name);
                entity.State = tmpState;
            }
            finally
            {
                _RWL.ReleaseWriterLock();
            }

		}

		public void Process(CreateReferenceCommand c)
		{
            _RWL.AcquireWriterLock(Timeout.Infinite);

            try
            {
                Entity parentEntity = _Entities[Utils.GetCacheKey(c.ParentType, c.ParentId)];
                if (parentEntity == null)
                    return;

                Entity childEntity = new Entity(c.ChildType);
                childEntity.Id = c.ChildId;

                State tmpState = parentEntity.State;
                parentEntity.AddValue(c.Role, childEntity, State.UpToDate);
                parentEntity.State = tmpState;
            }
            finally
            {
                _RWL.ReleaseWriterLock();
            }
		}

		public void Process(DeleteReferenceCommand c)
		{
            _RWL.AcquireWriterLock(Timeout.Infinite);

            try
            {
                Entity entity = _Entities[Utils.GetCacheKey(c.ParentType, c.ParentId)];
                if (entity == null)
                    return;

                for (int i = entity.EntityEntries.Count - 1; i >= 0; i--)
                {
                    EntityEntry ee = (EntityEntry)entity.EntityEntries[i];
                    if (ee.IsEntity && ((Entity)ee.Value).Id == c.ChildId)
                        entity.EntityEntries.Remove(ee);
                }
            }
            finally
            {
                _RWL.ReleaseWriterLock();
            }
		}
	}
}
